package com.carbon.auth.service.impl;

import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.util.RandomUtil;
import cn.hutool.core.util.StrUtil;
import cn.hutool.json.JSONUtil;
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import com.aliyuncs.CommonRequest;
import com.aliyuncs.CommonResponse;
import com.aliyuncs.DefaultAcsClient;
import com.aliyuncs.IAcsClient;
import com.aliyuncs.exceptions.ClientException;
import com.aliyuncs.http.MethodType;
import com.aliyuncs.profile.DefaultProfile;
import com.carbon.auth.service.SmsService;
import com.carbon.common.exception.CommonBizException;
import com.carbon.common.redis.RedisService;
import com.carbon.domain.common.constant.RedisKeyName;
import com.carbon.domain.common.constant.SmsConstant;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service;

import java.util.HashMap;
import java.util.concurrent.TimeUnit;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

@Slf4j
@Service
public class SmsServiceImpl implements SmsService {

    @Autowired
    private RedisService redisService;

    private static final String REGION_ID = "cn-hangzhou";
    private static final String SYS_DOMAIN = "dysmsapi.aliyuncs.com";
    private static final String SYS_VERSION = "2017-05-25";
    private static final String SYS_ACTION = "SendSms";

    // 签名（从配置文件读取）
    @Value("${sms.sign}")
    private String smsSignContent;
    @Value("${aliyun.accesskey-id}")
    private String accesskeyId;
    @Value("${aliyun.accesskey-secret}")
    private String accesskeySecret;

    // 1. 基础短信发送方法（无需修改，已正确对接阿里云）
    @Override
    public void sendMsg(String phone, String templateCode, String content) {
        DefaultProfile profile = DefaultProfile.getProfile(REGION_ID, accesskeyId, accesskeySecret);
        IAcsClient client = new DefaultAcsClient(profile);
        CommonRequest request = new CommonRequest();
        request.setSysMethod(MethodType.POST);
        request.setSysDomain(SYS_DOMAIN);
        request.setSysVersion(SYS_VERSION);
        request.setSysAction(SYS_ACTION);
        request.putQueryParameter("RegionId", REGION_ID);
        request.putQueryParameter("PhoneNumbers", phone); // 接收手机号
        request.putQueryParameter("SignName", smsSignContent); // 短信签名（需在阿里云备案）
        request.putQueryParameter("TemplateCode", templateCode); // 短信模板ID（需在阿里云备案）
        request.putQueryParameter("TemplateParam", content); // 模板参数（如：{"code":"123456"}）

        try {
            CommonResponse response = client.getCommonResponse(request);
            log.info("阿里云短信发送结果：{}", JSONUtil.toJsonPrettyStr(response.getData()));

            // 解析阿里云返回结果，判断是否发送成功
            JSONObject resultJson = JSON.parseObject(response.getData());
            // 阿里云返回"OK"表示请求成功（注意：需确保模板和签名已通过审核）
            if (!"OK".equals(resultJson.getString("Message"))) {
                log.error("短信发送失败：{}", resultJson.getString("Message"));
                throw new CommonBizException("短信发送失败：" + resultJson.getString("Message"));
            }
        } catch (ClientException e) {
            log.error("阿里云短信接口调用异常", e);
            throw new CommonBizException("短信服务异常，请稍后重试");
        }
    }

    // 2. 注册验证码发送（优化：随机生成6位验证码+启用短信+10分钟过期）
    @Override
    public void sendRegisterCode(String phone) {
        // 2.1 校验手机号格式（避免无效请求）
        String phoneRegex = "^1[3456789]\\d{9}$";
        // 编译正则表达式（Pattern是线程安全的，可提取为类常量重复使用）
        Pattern pattern = Pattern.compile(phoneRegex);
        // 创建匹配器，判断手机号是否符合规则
        Matcher matcher = pattern.matcher(phone);
        if (!matcher.matches()) { // matches()：判断整个字符串是否完全匹配正则
            throw new CommonBizException("请输入正确的11位手机号");
        }


        // 2.2 生成6位随机验证码（ Hutool工具类，无需自己写随机逻辑 ）
        String validCode = RandomUtil.randomNumbers(6);
        // 2.3 Redis键：区分业务（注册）+手机号，避免与其他验证码冲突
        String redisKey = RedisKeyName.SMS_REGISTER_KEY + phone;

        // 2.4 验证码存入Redis，设置10分钟过期（防刷+过期失效）
        redisService.setEx(redisKey, validCode, 10, TimeUnit.MINUTES);
        log.info("注册验证码已存入Redis：key={}, code={}", redisKey, validCode);

        // 2.5 构造短信模板参数（需与阿里云模板变量一致，如模板是"您的验证码是${code}"，则key必须是"code"）
        HashMap<String, Object> templateParam = new HashMap<>();
        templateParam.put("code", validCode);
        String paramJson = JSONUtil.toJsonStr(templateParam);

        // 2.6 调用阿里云短信接口发送（取消注释，启用实际发送）
        sendMsg(phone, SmsConstant.SMS_TEMPLATE_REGISTER, paramJson);
    }

    // 3. 忘记密码验证码发送（同注册逻辑，仅Redis键和模板不同）
    @Override
    public void sendForgotPasswordCode(String phone) {
        // 3.1 手机号格式校验
        String phoneRegex = "^1[3456789]\\d{9}$";
        // 编译正则表达式（Pattern是线程安全的，可提取为类常量重复使用）
        Pattern pattern = Pattern.compile(phoneRegex);
        // 创建匹配器，判断手机号是否符合规则
        Matcher matcher = pattern.matcher(phone);
        if (!matcher.matches()) { // matches()：判断整个字符串是否完全匹配正则
            throw new CommonBizException("请输入正确的11位手机号");
        }


        // 3.2 生成6位随机验证码
        String validCode = RandomUtil.randomNumbers(6);
        // 3.3 Redis键：区分业务（忘记密码）
        String redisKey = RedisKeyName.SMS_FORGOT_PASSWORD_KEY + phone;

        // 3.4 存入Redis，10分钟过期（修复原60000秒的错误）
        redisService.setEx(redisKey, validCode, 10, TimeUnit.MINUTES);
        log.info("忘记密码验证码已存入Redis：key={}, code={}", redisKey, validCode);

        // 3.5 构造模板参数
        HashMap<String, Object> templateParam = new HashMap<>();
        templateParam.put("code", validCode);
        String paramJson = JSONUtil.toJsonStr(templateParam);

        // 3.6 发送短信（注意：忘记密码需单独的模板，需在SmsConstant中定义）
        sendMsg(phone, SmsConstant.SMS_TEMPLATE_FORGOT_PASSWORD, paramJson);
    }

    // 4. 验证码校验方法（无需修改，已正确）
    @Override
    public void checkValidateCode(String phone, String code, String type) {
        if (StrUtil.isEmpty(phone) || StrUtil.isEmpty(code)) {
            throw new CommonBizException("手机号或验证码不能为空");
        }
        // 拼接Redis键（type是业务类型，如RedisKeyName.SMS_REGISTER_KEY）
        String redisKey = type + phone;
        String redisCode = redisService.get(redisKey);

        // 校验逻辑：过期/不匹配
        if (StrUtil.isEmpty(redisCode)) {
            throw new CommonBizException("验证码已过期，请重新获取");
        }
        if (!code.equals(redisCode)) {
            throw new CommonBizException("验证码错误，请重新输入");
        }

        // 校验通过后删除Redis（验证码一次性使用，防止重复提交）
        redisService.delete(CollUtil.newArrayList(redisKey));
        log.info("验证码校验通过，已删除Redis：key={}", redisKey);
    }
}